﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace Gobang.ToolClass.PVE
{
    /// <summary>
    /// 博弈树搜索
    /// </summary>
    public class GameTreeSearch
    {
        // 定义评估函数中的权重
        private readonly Dictionary<string, int> SCORES = new Dictionary<string, int> 
        {
            { "11111", 100000},  // 连五
            {"011110", 10000 },  // 活四
            {"011112", 10000 },
            {"211110", 5000 },   // 冲四
            {"01110", 1000 },    // 活三
            {"01112", 1000 },
            {"21110", 500 },     // 冲三
            {"11101", 500 },
            {"11011", 500 },
            { "11110", 500 },
            { "0110", 100 },      // 活二
            {"00110", 100 },
            {"01100", 100 },
            {"2110", 50 },       // 冲二
            {"2101", 50 },
            { "2011", 50 },
            {"1100", 50 },
            { "1001", 50 }
        };
        private int GetSCORES(string key,int def=0)
        {
            if(SCORES.ContainsKey(key))
                return SCORES[key];

            return def;
        }
        private readonly int[,] _board;
        private readonly int _boardSize;
        public GameTreeSearch(int[,] board)
        {
            _board= board;
            _boardSize= board.GetLength(0);
        }
        /// <summary>
        /// 定义棋盘评估函数
        /// </summary>
        /// <param name="player"></param>
        /// <returns></returns>
        private int Evaluate(int player)
        {
            int score = 0;
            int temp = _boardSize - 5;
            for (int i = 0; i < _boardSize; i++)
            {
                for (int j = 0; j < _boardSize; j++)
                {
                    if (_board[i, j] != player)
                        continue;
                    // 水平方向
                    if(j<= temp)
                    {
                        string line= CreateLine(i, j, 0);
                        score += GetSCORES(line);
                    }
                    // 垂直方向
                    if (i <= temp)
                    {
                        string line = CreateLine(i, j, 1);
                        score += GetSCORES(line);
                    }
                    // 左上到右下方向
                    if (i <= temp && j<= temp)
                    {
                        string line = CreateLine(i, j, 2);
                        score += GetSCORES(line);
                    }
                    // 右上到左下方向
                    if (i >=4 && j<= temp)
                    {
                        string line = CreateLine(i, j, 3);
                        score += GetSCORES(line);
                    }
                }
            }
            return score;
        }

        public int MiniMax(int depth,int alpha,int beta,bool maximizingPlayer)
        {
            if (depth == 0)
                return Evaluate(1) - Evaluate(-1);

            if (maximizingPlayer)
            {
                int maxEval = int.MinValue;
                for (int i = 0; i < _boardSize; i++)
                {
                    for (int j = 0; j < _boardSize; j++)
                    {
                        if (_board[i, j] != 0)
                            continue;
                        _board[i, j] = 1;
                        int temp= MiniMax(depth-1,alpha,beta,false);
                        _board[i, j] = 0;
                        maxEval=Math.Max(maxEval,temp);
                        alpha = Math.Max(alpha, temp);
                        if (beta <= alpha)
                            break;
                    }
                }
                return maxEval;
            }
            else
            {
                int minEval = int.MaxValue;
                for (int i = 0; i < _boardSize; i++)
                {
                    for (int j = 0; j < _boardSize; j++)
                    {
                        if (_board[i, j] != 0)
                            continue;
                        _board[i, j] = -1;
                        int temp = MiniMax(depth - 1, alpha, beta, true);
                        _board[i, j] = 0;
                        minEval=Math.Min(minEval,temp);
                        beta = Math.Min(beta, temp);
                        if (beta <= alpha)
                            break;
                    }
                }
                return minEval;
            }
        }
        /// <summary>
        /// 生成Line
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        /// <param name="dir">方向，0：水平方向；1：垂直方向；2：左上到右下方向；3：右上到左下方向</param>
        /// <returns></returns>
        private string CreateLine(int i,int j,int dir)
        {
            int[] lineArr= new int[5];
            for (int k = 0; k < 5; k++)
            {
                lineArr[k] = dir switch
                {
                    0 => _board[i, j + k],
                    1 => _board[i + k, j],
                    2 => _board[i + k, j + k],
                    3 => _board[i - k, j + k]
                };
            }
            return string.Join("", lineArr);
        }
        public Seat Move()
        {
            int maxEval = int.MaxValue;
            (int x, int y) = (0, 0);
            for (int i = 0; i < _boardSize; i++)
            {
                for (int j = 0; j < _boardSize; j++)
                {
                    if (_board[i, j] != 0)
                        continue;
                    _board[i, j] = 1;
                    int temp = MiniMax(3, int.MinValue, int.MaxValue, false);
                    _board[i, j] = 0;
                    if (temp > maxEval)
                    {
                        maxEval= temp;
                        (x,y)= (i, j);
                    }
                }
            }
            return new Seat(x,y, maxEval);
        }
    }
}
